// Pipeline Component Library Example
// ----------------------------------
// Colin Bowern (colinbowern@hotmail.com)
// January 2007
//
// Copyright (c) 2007 Colin Bowern
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// * The above copyright notice and this permission notice shall be included in
//   all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

#region References
using Microsoft.Practices.EnterpriseLibrary.Logging;
using System;
using System.Collections.Generic;
using System.Data.Odbc;
using System.Diagnostics;
using System.Globalization;
using System.Runtime.Serialization;
using System.Text;
using System.Text.RegularExpressions;
#endregion

namespace RockstarGuys.PipelineComponents
{
    /// <summary>
    /// Utility functions used by classes inside the assembly.
    /// </summary>
    internal static class Helper
    {
        #region Fields
        private static readonly string[] logCategories = { "Business Layer", "Pipeline Components" };
        #endregion

        #region Methods
        #region Casting/Conversion Methods
        /// <summary>
        /// Converts the ODBC connection string to SQL Client.
        /// </summary>
        /// <param name="connectionString">The connection string.</param>
        /// <returns></returns>
        public static string ConvertOdbcToSqlConnectionString(string connectionString)
        {
            Helper.CheckEmptyString(connectionString, "connectionString");

            OdbcConnectionStringBuilder connectionStringBuilder = new OdbcConnectionStringBuilder(connectionString);
            connectionStringBuilder.Remove("Provider");

            string returnValue = connectionStringBuilder.ToString();
            return returnValue;
        }

        /// <summary>
        /// Converts the string representation of a Guid to its Guid 
        /// equivalent. A return value indicates whether the operation 
        /// succeeded. 
        /// </summary>
        /// <param name="value">A string containing a Guid to convert.</param>
        /// <param name="result">
        /// When this method returns, contains the Guid value equivalent to 
        /// the Guid contained in <paramref name="s"/>, if the conversion 
        /// succeeded, or <see cref="Guid.Empty"/> if the conversion failed. 
        /// The conversion fails if the <paramref name="s"/> parameter is a 
        /// <see langword="null" /> reference (<see langword="Nothing" /> in 
        /// Visual Basic), or is not of the correct format. 
        /// </param>
        /// <value>
        /// <see langword="true" /> if <paramref name="value"/> was converted 
        /// successfully; otherwise, <see langword="false" />.
        /// </value>
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1021:AvoidOutParameters", MessageId = "1#")]
        public static bool GuidTryParse(string value, out Guid result)
        {
            bool isValid;
            if ((!string.IsNullOrEmpty(value)) && RegularExpressionObjects.GuidFormat.IsMatch(value))
            {
                result = new Guid(value);
                isValid = true;
            }
            else
            {
                result = Guid.Empty;
                isValid = false;
            }
            return isValid;
        }
        #endregion

        #region Logging Methods
        /// <summary>
        /// Logs the specified event.
        /// </summary>
        /// <param name="eventId">The event id.</param>
        /// <param name="severity">The severity.</param>
        /// <param name="extendedProperties">The extended properties.</param>
        /// <param name="category">The category.</param>
        /// <param name="message">The message.</param>
        public static void Log(int eventId, TraceEventType severity, Dictionary<string, object> extendedProperties, string category, string message)
        {
            Log(eventId, severity, extendedProperties, category, message, null);
        }

        /// <summary>
        /// Logs the specified event.
        /// </summary>
        /// <param name="eventId">The event id.</param>
        /// <param name="severity">The severity.</param>
        /// <param name="extendedProperties">The extended properties.</param>
        /// <param name="category">The category.</param>
        /// <param name="exception">The exception.</param>
        public static void Log(int eventId, TraceEventType severity, Dictionary<string, object> extendedProperties, string category, Exception exception)
        {
            Log(eventId, severity, extendedProperties, category, exception.ToString(), null);
        }

        /// <summary>
        /// Logs the specified event.
        /// </summary>
        /// <param name="eventId">The event id.</param>
        /// <param name="severity">The severity.</param>
        /// <param name="extendedProperties">The extended properties.</param>
        /// <param name="category">The category.</param>
        /// <param name="message">The message. This parameter cannot be null.</param>
        /// <param name="args">Arguments used to format the message.</param>
        public static void Log(int eventId, TraceEventType severity, Dictionary<string, object> extendedProperties, string category, string message, params object[] args)
        {
            Helper.CheckForNullReference(message, "message");

            LogEntry logEntry = new LogEntry();

            foreach (string categoryName in logCategories)
            {
                logEntry.Categories.Add(categoryName);
            }
            if (!string.IsNullOrEmpty(category))
            {
                logEntry.Categories.Add(category);
            }

            logEntry.EventId = eventId;
            logEntry.Severity = severity;

            if (Logger.ShouldLog(logEntry))
            {
                if ((extendedProperties != null) && (extendedProperties.Count > 0))
                {
                    foreach (KeyValuePair<string, object> keyValuePair in extendedProperties)
                    {
                        logEntry.ExtendedProperties.Add(keyValuePair);
                    }
                }

                if (args != null)
                {
                    logEntry.Message = String.Format(CultureInfo.InvariantCulture, message, args);
                }
                else
                {
                    logEntry.Message = message;
                }

                Logger.Write(logEntry);
            }
        }
        #endregion

        #region Math
        /// <summary>
        /// Round currency values based on the degree of precision.
        /// </summary>
        /// <param name="precision">The precision.</param>
        /// <param name="value">Value.</param>
        /// <returns></returns>
        public static decimal Round(int precision, decimal value)
        {
            // if the precision level is greater than 0 then round
            // the currency value to the desired number of significant
            // digits. Otherwise (negative precision level) divide by
            // 10^(precision level), round dropping digits to the right
            // of the decimal, and then multiply by 10*(precision level)
            //
            // For example if precision == -2 and currency == 1234.56 then
            // this algorithm will return 1200
            if (precision > 0)
            {
                return Convert.ToDecimal(Math.Round(value, precision));
            }
            else
            {
                Int32 f = 1;
                while (precision < 0)
                {
                    f *= 10;
                    precision += 1;
                }
                value /= f;
                value = Math.Round(value);
                value *= f;
            }

            return value;
        }
        #endregion

        #region Validation
        /// <summary> 
        ///     Checks for <see langword="null"/> or <see cref="DBNull"/> reference.
        /// </summary>
        /// <remarks>
        ///     The <see langword="null"/> and <see cref="DBNull"/> values are used to
        ///     indiciate that a value has not been set for a variable.  This function
        ///     is useful if you require that a variable requires a value to
        ///     evaluate it.
        /// </remarks>
        /// <param name="variable">Variable</param>
        /// <param name="variableName">Name of the variable</param>
        /// <exception cref="ArgumentNullException">
        ///		Thrown if <paramref name="variable"/> is <see langword="null"/> or <see cref="DBNull"/>.
        ///		<para>- or -</para>
        ///		<paramref name="variableName"/> is <see langword="null"/>.
        /// </exception>
        public static void CheckForNullReference(object variable, string variableName)
        {
            if (variableName == null)
            {
                throw new ArgumentNullException("variableName");
            }

            if ((variable == null) || (variable == DBNull.Value))
            {
                throw new ArgumentNullException(variableName);
            }
        }

        /// <summary>
        /// Checks the range.
        /// </summary>
        /// <param name="variable">The variable.</param>
        /// <param name="variableName">Name of the variable.</param>
        /// <param name="minValue">The min value.</param>
        /// <param name="maxValue">The max value.</param>
        /// <exception cref="ArgumentNullException">
        ///		Thrown if <paramref name="variable"/> is <see langword="null"/>.
        ///		<para>- or -</para>
        /// 	<paramref name="variableName"/> is <see langword="null"/>.
        ///		<para>- or -</para>
        /// 	<paramref name="minValue"/> is <see langword="null"/>.
        ///		<para>- or -</para>
        /// 	<paramref name="maxValue"/> is <see langword="null"/>.
        /// </exception>
        /// <exception cref="ArgumentOutOfRangeException">
        ///		Thrown if <paramref name="variable"/> is not between <paramref name="minLength"/> and <paramref name="maxLength"/>.
        /// </exception>
        public static void CheckRange<T>(T variable, string variableName, T minValue, T maxValue)
        {
            CheckForNullReference(variableName, "variableName");
            CheckForNullReference(variable, variableName);
            CheckForNullReference(minValue, "minValue");
            CheckForNullReference(maxValue, "maxValue");

            Comparer<T> TypeComparer = Comparer<T>.Default;
            int Result = 0;

            Result = TypeComparer.Compare(variable, minValue);
            if (Result < 0)
            {
                throw new ArgumentOutOfRangeException(variableName, variable, String.Format(CultureInfo.InvariantCulture, StringResources.ExceptionOutOfRangeTooSmall, minValue));
            }

            Result = TypeComparer.Compare(variable, maxValue);
            if (Result > 0)
            {
                throw new ArgumentOutOfRangeException(variableName, variable, String.Format(CultureInfo.InvariantCulture, StringResources.ExceptionOutOfRangeTooLarge, minValue));
            }
        }

        /// <summary>
        /// Checks the length of the string.
        /// </summary>
        /// <param name="variable">The variable.</param>
        /// <param name="variableName">Name of the variable.</param>
        /// <param name="minLength">Length of the min.</param>
        /// <param name="maxLength">Length of the max.</param>
        /// <exception cref="ArgumentNullException">
        ///		Thrown if <paramref name="variable"/> is <see langword="null"/>.
        ///		<para>- or -</para>
        /// 	<paramref name="variableName"/> is <see langword="null"/>.
        /// </exception>
        /// <exception cref="ArgumentOutOfRangeException">
        ///		Thrown if <paramref name="variable"/> is not between <paramref name="minLength"/> and <paramref name="maxLength"/>.
        ///		<para>- or -</para>
        /// 	<paramref name="minLength"/> is less than zero.
        ///		<para>- or -</para>
        /// 	<paramref name="maxLength"/> is larger than the maximum integer value.
        /// </exception>
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1062:ValidateArgumentsOfPublicMethods")]
        public static void CheckStringLength(string variable, string variableName, int minLength, int maxLength)
        {
            CheckForNullReference(variableName, "variableName");
            CheckForNullReference(variable, variableName);
            CheckRange<int>(minLength, "minLength", 0, maxLength);
            CheckRange<int>(maxLength, "maxLength", minLength, int.MaxValue);

            if (variable.Length < minLength)
            {
                throw new ArgumentOutOfRangeException(variableName, variable, String.Format(CultureInfo.InvariantCulture, StringResources.ExceptionOutOfRangeTooSmall, minLength));
            }

            if (variable.Length > maxLength)
            {
                throw new ArgumentOutOfRangeException(variableName, variable, String.Format(CultureInfo.InvariantCulture, StringResources.ExceptionOutOfRangeTooLarge, maxLength));
            }
        }

        /// <summary>
        /// Checks the empty string.
        /// </summary>
        /// <param name="variable">The variable.</param>
        /// <param name="variableName">Name of the variable.</param>
        /// <exception cref="ArgumentNullException">
        ///		Thrown if <paramref name="variable"/> is <see langword="null"/>.
        ///		<para>- or -</para>
        /// 	<paramref name="variableName"/> is <see langword="null"/>.
        /// </exception>
        /// <exception cref="ArgumentOutOfRangeException">
        ///		Thrown if <paramref name="variable"/> is empty.
        /// </exception>
        public static void CheckEmptyString(string variable, string variableName)
        {
            CheckStringLength(variable, variableName, 1, int.MaxValue);
        }

        /// <summary>
        /// Check <paramref name="variable"/> to determine if it is a valid
        /// format as specified by <paramref name="regularExpression"/>.
        /// </summary>
        /// <param name="variable">The string value to check.</param>
        /// <param name="variableName">The name of the variable being checked.</param>
        /// <param name="regularExpression">The regular expression.</param>
        /// <param name="ignoreBlankValues"><see langword="true"/> to ignore blank values; otherwise <see langword="false"/>.</param>
        /// <remarks>
        /// Before checking the <paramref name="regularExpression"/>, a call is made to
        /// <see cref="CheckForNullReference"/>.
        /// </remarks>
        /// <exception cref="ArgumentNullException">
        /// Thrown if <paramref name="variable"/> is <see langword="null"/>.
        /// <para>- or -</para>
        /// 	<paramref name="variableName"/> is <see langword="null"/>.
        /// <para>- or -</para>
        /// 	<paramref name="regularExpression"/> is <see langword="null"/>.
        /// </exception>
        /// <exception cref="ArgumentException">
        /// Thrown if <paramref name="variable"/> does not match is the <paramref name="regularExpression"/> criteria.
        /// </exception>
        public static void CheckStringFormat(string variable, string variableName, Regex regularExpression, bool ignoreBlankValues)
        {
            CheckForNullReference(variableName, "variableName");
            CheckForNullReference(variable, variableName);
            CheckForNullReference(regularExpression, "regularExpression");

            if ((ignoreBlankValues) && (variable.Length == 0))
            {
                return;
            }

            if (!regularExpression.IsMatch(variable))
            {
                throw new ArgumentFormatException(variableName, variable, regularExpression.ToString());
            }
        }
        #endregion
        #endregion
    }
}
